package com.chenbei.erp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chenbei.common.domain.BaseEntity;
import com.chenbei.erp.domain.Batches;
import com.chenbei.erp.domain.Product;
import com.chenbei.erp.domain.Supplier;
import com.chenbei.erp.mapper.BatchesMapper;
import com.chenbei.erp.service.IBatchesService;
import com.chenbei.erp.service.IProductService;
import com.chenbei.erp.service.ISupplierService;
import com.chenbei.erp.vo.req.BatchesReq;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

@Service
public class BatchesService extends ServiceImpl<BatchesMapper, Batches> implements IBatchesService {

    @Autowired
    private IProductService productService;

    @Autowired
    private ISupplierService supplierService;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void add(Batches batches) {
        String lockName = "wms_batched_add_lock_" + LocalDate.now();
        RLock lock = redissonClient.getLock(lockName);
        try {
            boolean isLocked = lock.tryLock(5, 10, TimeUnit.SECONDS);

            if (!isLocked) {
                throw new RuntimeException("Failed to acquire lock for add operation");
            }

            LambdaQueryWrapper<Batches> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            LocalDate now = LocalDate.now();
            lambdaQueryWrapper.ge(BaseEntity::getCreateTime, now);
            long count = count(lambdaQueryWrapper);

            String code = generateCode(batches.getSupplierId(), batches.getProductId(), (int)count + 1);
            batches.setCode(code);

            save(batches);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Thread interrupted while acquiring lock", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public void update(Batches batches) {
        String lockName = "wms_batched_update_lock_" + LocalDate.now();
        RLock lock = redissonClient.getLock(lockName);
        try {
            boolean isLocked = lock.tryLock(5, 10, TimeUnit.SECONDS);

            if (!isLocked) {
                throw new RuntimeException("Failed to acquire lock for add operation");
            }

            LambdaUpdateWrapper<Batches> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            lambdaUpdateWrapper.set(Batches::getQuantity, batches.getQuantity())
                    .set(Batches::getStatus, batches.getStatus())
                    .eq(Batches::getId, batches.getId());
            update(lambdaUpdateWrapper);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Thread interrupted while acquiring lock", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public Page<Batches> pageList(BatchesReq batchesReq) {
        LambdaQueryWrapper<Batches> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        Page<Batches> pageConfig = new Page<>(batchesReq.getPageNum(), batchesReq.getPageSize());

        if (batchesReq.getProductId() != null) {
            lambdaQueryWrapper.eq(Batches::getProductId, batchesReq.getProductId());
        }
        return page(pageConfig, lambdaQueryWrapper);
    }

    /**
     * 生成批次号
     * @param supplierId 供应商ID
     * @param productId 产品ID
     * @param serialNo 序列号
     * @return String
     */
    private String generateCode(Long supplierId, Long productId, Integer serialNo) {
        Supplier supplier = supplierService.getById(supplierId);
        Product product = productService.getById(productId);

        return supplier.getCode() + product.getCode() + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + String.format("%03x" ,serialNo);
    }
}
